#ifndef __ROOM_HPP__
#define __ROOM_HPP__
#include "util.hpp"
#include "online.hpp"
#include "logger.hpp"
#include "db.hpp"
#define BOARD_ROW 15
#define CHESS_WHITE 1 // 白棋
#define CHESS_BLACK 2 // 黑棋
typedef enum
{
    GAME_START,
    GAME_OVER
} room_statu;
class room
{
private:
    uint64_t _room_id;
    room_statu _statu;
    int _player_count;
    uint64_t _white_id;
    uint64_t _black_id;
    user_table *_tb_user;
    online_manager *_online_user;
    std::vector<std::vector<int>> _board;

private:
    // 返回胜利玩家的id,没有人胜利则返回0
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        // row and col 是下棋位置, row_off和col_off是偏移量
        int count = 1;
        int search_row = row + row_off;
        int search_col = col + col_off;
        // 1.向一个方向搜索
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_ROW)
        {
            if (_board[search_row][search_col] == color)
            {
                count++;
            }
            else
            {
                break;
            }
            // 检索位置继续偏移
            search_row += row_off;
            search_col += col_off;
        }
        // 2.向另一个方向搜索
        search_row = row - row_off;
        search_col = col - col_off;
        while (search_row >= 0 && search_row < BOARD_ROW &&
               search_col >= 0 && search_col < BOARD_ROW)
        {
            if (_board[search_row][search_col] == color)
            {
                count++;
            }
            else
            {
				
                break;
            }
            // 检索位置继续偏移
            search_row -= row_off;
            search_col -= col_off;
        }
        // 3.判断是否有玩家胜利
        if (count >= 5)
        {
            // 有玩家胜利
            return true;
        }

        // 没有玩家胜利
        return false;
    }
    uint64_t check_win(int row, int col, int color)
    {
        // 判断是否有玩家胜利(从走棋位置开始判断是否有五星连珠)
        // 1.横向判断,统计下棋位置右边同色数量的棋子,行不变,列++,统计下棋位置左边的同色数量的棋子,行不变,列--
        // five(row,col,0,1,color);
        // 2.纵向判断,统计下棋位 置上边同色数量的棋子,行--,列不变,统计下棋位置下边的同色数量的棋子,行++,列不变
        // five(row,col,1,0 ,color);
        // 3.右斜判断,右上:行--,列++,左下:行++,列--
        // five(row,col,-1,1,color);
        // 4.左斜判断:左上:行--,列--,右下:行++,列++
        // five(row,col,-1,-1,color);
        if (five(row, col, 0, 1, color) || five(row, col, 1, 0, color) ||
            five(row, col, -1, 1, color) || five(row, col, -1, -1, color))
        {
            // 有玩家胜利
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
       return 0;    
    }

public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user)
        : _room_id(room_id), _statu(GAME_START), _player_count(0),
          _tb_user(tb_user), _online_user(online_user),
          _board(BOARD_ROW, std::vector<int>(BOARD_ROW, 0))
    {
        DLOG("room %lu create", _room_id);
    }

    ~room() { DLOG("room %lu destroy", _room_id); }
    uint64_t id()
    {
        return _room_id;
    }
    room_statu statu() { return _statu; }
    int player_count() { return _player_count; }
    void add_white_user(uint64_t uid)
    {
        _white_id = uid;
        _player_count++;
    }
    void add_black_user(uint64_t uid)
    {
        _black_id = uid;
        _player_count++;
    }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    
    // 处理聊天动作
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value json_resp = req;
        // 1.当前请求的房间号是否与当前的房间号相匹配
        uint64_t room_id = req["room_id"].asUInt64();
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "room id not match"; // 房间号不匹配
            return json_resp;
        }
        // 2.检测消息中是否包含敏感词
        std::string msg = req["message"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "message contains sensitive words"; // 消息中包含敏感词
            return json_resp;
        }

        // 3.广播--返回消息
        json_resp["result"] = true;
        return json_resp;
    }
    // 处理玩家退出房间动作
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resp;
        // 如果是在下棋中退出,那么对方胜利,否则是下棋结束了退出,则是正常退出
        if (_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(uid == _white_id ? _black_id : _white_id);


            json_resp["optype"] = "put_chess";
            json_resp["result"] = false;
            json_resp["reason"] = "opponent offline,you Win!!!"; // 对方掉线
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;

            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;

            // 广播
            broadcast(json_resp);
        }
        // 房间中玩家数量--
        _player_count--;
        return;
    }

     // 处理下棋动作
    Json::Value handle_chess(Json::Value &req)
    {
        DLOG("进入handle——chess");
        Json::Value json_resp = req;
        // 2.判断两个玩家是否都在线,任意一个不在线,另一个就胜利
        uint64_t cur_uid = req["uid"].asUInt64();
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();
        if (_online_user->is_in_game_room(_white_id) == false)
        {
            // 此时就是掉线了,判定另一边玩家胜利
            json_resp["result"] = true;
            json_resp["reason"] = "opponent offline,you Win!!!"; // 对方掉线
            json_resp["winner"] = (Json::UInt64)(_black_id);
            _tb_user->win(_black_id);
            _tb_user->lose(_white_id);
            return json_resp;
        }
        if (_online_user->is_in_game_room(_black_id) == false)
        {
            // 此时就是掉线了,判定另一边玩家胜利
            json_resp["result"] = true;
            json_resp["reason"] = "opponent offline,you Win!!!"; // 对方掉线
            json_resp["winner"] = (Json::UInt64)(_white_id);

            _tb_user->win(_white_id);
            _tb_user->lose(_black_id);
            return json_resp;
        }
        // 3.获取走棋的位置,判断房间是否合理
        if (_board[chess_row][chess_col] != 0)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "this position is already occupied"; // 此位置已经被占用
            return json_resp;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_color;
        // 4.判断是否有玩家胜利(从走棋位置开始判断是否有五星连珠)
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if (winner_id != 0)
        {
            json_resp["reason"] = "five stars connected!!! you Win!!!";
        }
        json_resp["result"] = true;
        json_resp["winner"] = (Json::UInt64)winner_id;
        return json_resp;
    }
    // 总的请求处理函数,在函数内部区分请求类型,
    void handle_request(Json::Value &req)
    {
        DLOG("我是handle_request,开始处理了");
        // 根据不同的请求类型调用不同的函数
        Json::Value json_resp;


        std::string req_body;
        json_util::serialize(req,req_body);
        DLOG("请求体: %s",req_body.c_str());

        // 1.当前请求的房间号是否与当前的房间号相匹配
        uint64_t room_id = req["room_id"].asUInt64();
        std::cout<<  "请求体中的roomid: " <<room_id<<std::endl;
        std::cout<< "房间中的roomid: "<< _room_id<<std::endl;
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "room id not match"; // 房间号不匹配

            return broadcast(json_resp);
        }
        // 2.判断请求的类型
        if (req["optype"].asString() == "put_chess")
        {
            DLOG("开始下棋了");
            json_resp = handle_chess(req);
            if (json_resp["winner"].asUInt64() != 0)
            {
                uint64_t winner_id = json_resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req);
        }
        else
        {
            // 此时就是就问题
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型"; // 请求类型不匹配
        }
        std::string body;
        json_util::serialize(json_resp,body);
        DLOG("房间-广播动作开始:%s",body.c_str());
        return broadcast(json_resp);
        
    }

    // 将指定的信息广播给房间的所有玩家
    void broadcast(Json::Value &rsp)
    {
        // 1.对要响应的信息进行序列化,将Json::Value转换为string
        std::string body;
        json_util::serialize(rsp, body);
        // 2.获取房间类所有用户的连接
        wsserver_t::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);
        if (wconn.get() != nullptr)
        {
            // 发送数据,第二个参数默认是text类型
            wconn->send(body);
        }else{
            DLOG("房间-白棋玩家连接获取失败!");
        }
        wsserver_t::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);
        if (bconn.get() != nullptr)
        {
            // 发送数据,第二个参数默认是text类型
            bconn->send(body);
        }else{
            DLOG("房间-黑棋玩家连接获取失败!");
        }
        return ;
        // 3.发送响应信息
    }
};

using room_ptr = std::shared_ptr<room>;
class room_manager
{
    private:
        uint64_t _next_rid;
        std::mutex _mutex;
        user_table* _tb_user;
        online_manager* _online_user;
        std::unordered_map<uint64_t, room_ptr> _rooms; // 房间id和房间对象的映射关系
        std::unordered_map<uint64_t, uint64_t> _users; // 用户id和房间id的映射关系
    public:
    // 构造函数 初始化房间ID计数器
        room_manager(user_table *ut,online_manager* om):
            _next_rid(1),_tb_user(ut),_online_user(om){
            DLOG("room manager create");
        }
        ~room_manager(){
            DLOG("room manager destroy");
        }
        
        room_ptr create_room(uint64_t uid1,u_int64_t uid2) // 为两个用户创建房间,并通过房间的智能指针管理对象
        {
            // 两个用户在游戏大厅中匹配成功之后就创建房间
            // 1.校验两个用户是否都在游戏大厅中,都在才需要创建房间
            if(_online_user->is_in_game_hall(uid1) == false || _online_user->is_in_game_hall(uid2) == false){
                DLOG("user %lu or %lu not in game hall",uid1,uid2);
                return room_ptr(); // 返回空指针
            }
            // 2.  创建房间,将用户信息添加到房间中
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            room_ptr rp(new room(_next_rid,_tb_user,_online_user));
            rp->add_white_user(uid1);
            rp->add_black_user(uid2);
            // 3.将房间信息管理起来
            _rooms.insert(std::make_pair(_next_rid,rp)); // 房间ID和房间对象的映射关系
            _users.insert(std::make_pair(uid1,_next_rid)); // 用户ID和房间ID的映射关系
            _users.insert(std::make_pair(uid2,_next_rid)); // 用户ID和房间ID的映射关系
            _next_rid++; // 房间ID自增
            // 4.返回房间信息
            return rp;
        }
        room_ptr get_room_by_rid(uint64_t rid)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            auto it = _rooms.find(rid);
            if(it == _rooms.end()){
                return room_ptr(); // 返回空指针
            }
            return it->second; // 返回房间对象的智能指针
        }
        room_ptr get_room_by_uid(uint64_t uid)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            auto uit = _users.find(uid);
            if(uit == _users.end()){
                return room_ptr(); // 返回空指针
            }
            // 需要单独进行,不能调用get_room_by_rid,防止产生死锁
            uint64_t rid = uit->second;
            auto rit = _rooms.find(rid);
            if(rit == _rooms.end()){
                return room_ptr(); // 返回空指针
            }
            return rit->second; // 返回房间对象的智能指针
        }
        void remove_room(uint64_t rid) // 销毁房间
        {
            // 因为房间信息是通过shared_ptr进行管理的,因此只要将shared_ptr从rooms中移除即可.
            // 1.获取房间id获取房间信息
            room_ptr rp = get_room_by_rid(rid);
            if(rp.get() == nullptr){
                DLOG("room %lu not exist",rid);
                return;
            }
            // 2.通过房间信息,获取房间所有用户的id
            uint64_t uid1 = rp->get_white_user();
            uint64_t uid2 = rp->get_black_user();
            // 3.移除房间管理中的用户信息
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            _users.erase(uid1); // 移除用户
            _users.erase(uid2); // 移除用户
            // 4.移除房间信息
            _rooms.erase(rid); // 移除房间
        }
        void remove_room_user(uint64_t uid)// 移除房间的中的用户,用户连接断开的时候调用
        {
            // 1.获取房间id
            room_ptr rp = get_room_by_uid(uid);
            if(rp.get() == nullptr){
                DLOG("user %lu not in room",uid);
                return;
            }
            uint64_t rid = rp->id(); // 获取房间id
            // 2.移除房间中的用户
            rp->handle_exit(uid);
            // 3.判断房间中是否还有用户,如果没有用户了,就销毁房间
            if(rp->player_count() == 0){
                remove_room(rid);
            }
            return;
        }
};

#endif
